package mongo

import (
	"context"
	"fmt"
	"strings"

	"go.mongodb.org/mongo-driver/event"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"

	"gitee.com/csingo/cLog"

	"gitee.com/csingo/cDB/drivers"
)

type Connector struct {
	connection string
	config     *Connection
	db         *mongo.Database
}

func (i *Connector) GetConnection() string {
	return i.connection
}

func (i *Connector) GetDriver() drivers.DriverInterface {
	return i.config
}

func (i *Connector) GetDB() any {
	return i.db
}

func (i *Connector) Connect() error {
	var err error
	var dsn strings.Builder

	if i.config == nil {
		return fmt.Errorf("mongo client config is nil")
	}

	dsn.WriteString("mongodb://")
	dsn.WriteString(i.config.Username)
	dsn.WriteString(":")
	dsn.WriteString(i.config.Password)
	dsn.WriteString("@")
	dsn.WriteString(strings.Join(i.config.Hosts, ","))
	if i.config.AuthDB != "" {
		dsn.WriteString("/")
		dsn.WriteString(i.config.AuthDB)
	} else if i.config.Database != "" {
		dsn.WriteString("/")
		dsn.WriteString(i.config.Database)
	}
	if len(i.config.Options) > 0 {
		var o []string
		for k, v := range i.config.Options {
			o = append(o, fmt.Sprintf("%s=%s", k, v))
		}
		dsn.WriteString("?")
		dsn.WriteString(strings.Join(o, "&"))
	}

	clientOpts := options.Client().ApplyURI(dsn.String())
	if i.config.Debug {
		clientOpts.SetMonitor(
			&event.CommandMonitor{
				Started: func(ctx context.Context, event *event.CommandStartedEvent) {
					cLog.WithContext(ctx, map[string]any{
						"command":  event.Command.String(),
						"database": event.DatabaseName,
					}).Debug("执行 mongodb 查询")
				},
				Succeeded: func(ctx context.Context, event *event.CommandSucceededEvent) {
					cLog.WithContext(ctx, map[string]any{
						"cost": event.Duration.Milliseconds(),
					}).Debug("查询成功")

				},
				Failed: func(ctx context.Context, event *event.CommandFailedEvent) {
					cLog.WithContext(ctx, map[string]any{
						"err": event.Failure,
					}).Debug("查询失败")
				},
			},
		)
	}

	var client *mongo.Client
	if client, err = mongo.Connect(context.Background(), clientOpts); err != nil {
		return err
	}

	i.db = client.Database(i.config.Database)

	cLog.WithContext(nil, map[string]any{
		"source":     "cDB.Connector.Connect",
		"connection": i.connection,
	}, []cLog.Option{
		cLog.IncludePaths{},
	}).Trace("创建 mongo 连接客户端")

	return nil
}

func NewMongoConnector(connection string, config *Connection) (*Connector, error) {
	connector := &Connector{
		connection: connection,
		config:     config,
		db:         nil,
	}
	err := connector.Connect()

	return connector, err
}
